<template>
  <div class="upload-excel">
    <div class="app-container">
      <el-card>
        <h2 style="text-align:center">员工导入</h2>
        <el-alert :title="message" type="warning" show-icon :closable="false" />
        <div class="upload-box">
          <div class="btn-upload">
            <el-button
              :loading="loading"
              size="mini"
              type="primary"
              @click="handleUpload"
            >
              点击上传
            </el-button>
          </div>

          <input
            ref="excel-upload-input"
            class="excel-upload-input"
            type="file"
            accept=".xlsx, .xls"
            @change="handleClick"
          />
          <div
            class="drop"
            @drop="handleDrop"
            @dragover="handleDragover"
            @dragenter="handleDragover"
          >
            <i class="el-icon-upload" />
            <span>将文件拖到此处</span>
          </div>
        </div>
      </el-card>
    </div>
  </div>
</template>

<script>
import XLSX from 'xlsx' // 解析excel的插件
export default {
  name: 'UploadExcel',
  props: {
    // 父传子匹配的校验函数
    // eslint-disable-next-line vue/require-default-prop
    beforeUpload: Function,
    // 父传子匹配的成功函数
    // eslint-disable-next-line vue/require-default-prop
    onSuccess: Function,
  },
  data() {
    return {
      message: '每次导入仅可添加1000名员工，姓名、手机、入职时间、聘用形式为必填项',
      loading: false,
      excelData: {},
    }
  },
  methods: {
    // 一、点击上传按钮
    handleUpload() {
      // 触发表单的click事件
      this.$refs['excel-upload-input'].click()
    },

    // 二、表单的change事件
    handleClick(e) {
      // 1.选中文件后触发change事件
      // console.log(e.target)
      const files = e.target.files
      const rawFile = files[0]
      if (!rawFile) return false
      // console.log(rawFile)
      // 2.拿到文件后对象后进行下一步处理
      this.upload(rawFile)
    },

    // 三、获取文件后进行下一步处理的函数
    upload(rawFile) {
      // 1.先清空原有的文件内容
      this.$refs['excel-upload-input'].value = null
      // 2.判断有没有校验函数传递过来,没有就直接读取文件
      if (!this.beforeUpload) return this.readerData(rawFile)
      // 3.如果传递过来了函数,则利用该函数进行校验
      // 函数返回的是一个布尔值,通过校验则返回true,进行下一步读取文件操作,否则会有错误提示
      const before = this.beforeUpload(rawFile)
      if (before) return this.readerData(rawFile)
    },

    // 四、开始读取文件的方法
    readerData(rawFile) {
      // 1.roading状态开始
      this.loading = true
      // 封装promise
      return new Promise((resolve, reject) => {
        // h5中新增的, 读取文件的api, fileReader
        const reader = new FileReader()

        // 读文件需要时间, onload触发时, 才是文件读完的时候
        reader.onload = e => {
          const data = e.target.result
          const workbook = XLSX.read(data, { type: 'array' })
          const firstSheetName = workbook.SheetNames[0]
          const worksheet = workbook.Sheets[firstSheetName]
          const header = this.getHeaderRow(worksheet)
          const results = XLSX.utils.sheet_to_json(worksheet)
          this.generateData({ header, results })
          // 关闭加载状态, 读完了
          this.loading = false
          resolve()
        }
        reader.readAsArrayBuffer(rawFile)
      })
    },

    // 五、拖拽上传功能
    handleDrop(e) {
      e.stopPropagation()
      e.preventDefault()
      if (this.loading) return
      const files = e.dataTransfer.files
      if (files.length !== 1) {
        this.$message.error('Only support uploading one file!')
        return
      }
      const rawFile = files[0] // only use files[0]
      if (!this.isExcel(rawFile)) {
        this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
        return false
      }
      this.upload(rawFile)
      e.stopPropagation()
      e.preventDefault()
    },
    handleDragover(e) {
      e.stopPropagation()
      e.preventDefault()
      e.dataTransfer.dropEffect = 'copy'
    },

    // (1)封装的获取表头字段的方法
    getHeaderRow(sheet) {
      const headers = []
      const range = XLSX.utils.decode_range(sheet['!ref'])
      let C
      const R = range.s.r
      /* start in the first row */
      for (C = range.s.c; C <= range.e.c; ++C) {
        /* walk every column in the range */
        const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
        /* find the cell in the first row */
        let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
        if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
        headers.push(hdr)
      }
      return headers
    },

    // (2)封装的设置表头字段和对应结果的方法
    generateData({ header, results }) {
      this.excelData.header = header
      this.excelData.results = results
      // 调用父传子 上传成功的方法,将获取到的数据传递过去进行下一步处理
      this.onSuccess && this.onSuccess(this.excelData)
    },

    // (3)校验是否是excel表格的方法
    isExcel(file) {
      return /\.(xlsx|xls|csv)$/.test(file.name)
    },
  },
}
</script>

<style scoped lang="scss">
.upload-box {
  display: flex;
  justify-content: center;
  margin-top: 100px;
  .excel-upload-input {
    display: none;
    z-index: -9999;
  }
  .btn-upload,
  .drop {
    border: 1px dashed #bbb;
    width: 350px;
    height: 160px;
    text-align: center;
    line-height: 160px;
  }
  .drop {
    margin-left: 20px;
    padding-top: 20px;
    line-height: 80px;
    color: #bbb;
    i {
      font-size: 60px;
      display: block;
    }
  }
}
</style>
